﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Dapper;
using OS.Common.Core;
using OS.Common.Enums;
using OS.Common.Plugs.ConfigPlug;
using OS.Common.Plugs.LogPlug;
using OS.Common.Utils;
using OS.Orm.Extention;

namespace OS.Orm
{
    /// <summary>
    /// 仓储层基类
    /// </summary>
    public class BaseRepository
    {
        /// <summary>
        /// 数据库表名
        /// </summary>
        protected string SourceTableName;

        protected readonly string WriteConnectionString;
        protected readonly string ReaderConnectionString;

        /// <summary>
        /// 只知道接口，不知道实现，这样只关注接口中的方法。
        /// </summary>
        private readonly IConfigPlug _configPlug = IocContainer<IConfigPlug>.Instance;

        /// <summary>
        /// 目前只支持net core下的配置文件自动读取，非net core下需要手动传数据库连接字符串
        /// </summary>
        /// <param name="writeConnectionString">写数据库连接字符串</param>
        /// <param name="readerConnectionString">读数据库连接字符串</param>
        public BaseRepository(string writeConnectionString = null, string readerConnectionString = null)
        {
            this.WriteConnectionString = writeConnectionString ?? _configPlug.GetConnectionString("WriteConnection");
            this.ReaderConnectionString = readerConnectionString ?? _configPlug.GetConnectionString("ReadeConnection");
        }

        /// <summary>
        /// 获取生成数据库连接
        /// </summary>
        /// <param name="connectionString">数据库连接字符串</param>
        /// <returns></returns>
        protected virtual IDbConnection GetConnection(string connectionString)
        {
            return new SqlConnection(connectionString);
        }

        #region 底层基础读写分离

        /// <summary>
        /// 执行写数据库操作
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="func">方法和连接</param>
        /// <returns></returns>
        protected internal async Task<T> ExecuteWriteAsync<T>(Func<IDbConnection, Task<T>> func)
            where T : CustomResult, new() => await Execute(func, WriteConnectionString);

        /// <summary>
        /// 执行读操作，返回具体类型，自动包装成ResultMo结果实体
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="func">方法和连接字符串</param>
        /// <returns></returns>
        protected internal async Task<CustomResult<T>> ExecuteReadAsync<T>(Func<IDbConnection, Task<T>> func) =>
            await Execute(async connection =>
            {
                var result = await func(connection);
                return result != null
                    ? new CustomResult<T>(result)
                    : new CustomResult<T>(ResultType.ObjectNull, "未发现相关数据！");
            }, ReaderConnectionString);

        /// <summary>
        /// 执行读操作，直接继承自ResultMo实体
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="func">方法和连接字符串</param>
        /// <returns></returns>
        protected internal async Task<T> ExecuteReadResultAsync<T>(Func<IDbConnection, Task<T>> func)
            where T : CustomResult, new() => await Execute(func, ReaderConnectionString);

        /// <summary>
        /// 执行操作
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="func">方法和链接池</param>
        /// <param name="connectionString">连接数据库字符串</param>
        /// <returns></returns>
        internal async Task<T> Execute<T>(Func<IDbConnection, Task<T>> func, string connectionString)
            where T : CustomResult, new()
        {
            T t;
            try
            {
                using (var con = GetConnection(connectionString))
                {
                    t = await func(con);
                }
            }
            catch (Exception e)
            {
#if DEBUG
                throw e;
#else
                LogUtil.Error(string.Concat("数据库操作错误，详情：", e.Message, "\r\n", e.StackTrace), "DataRepositoryConnectionError", "DapperRepository");
                t = new T
                {
                    Code = (int)ResultType.InnerError,
                    Message = "数据更新出错！"
                };
                return t;
#endif
            }

            return t ?? new T() { Code = (int)ResultType.ObjectNull, Message = "未发现对应结果响应！" };
        }

        #endregion

        #region 基础CRUD操作方法

        /// <summary>
        /// 插入数据（默认Id自增长）
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="model">动态实体</param>
        /// <param name="isAuto">Id主键是否自增长</param>
        /// <returns></returns>
        public virtual async Task<CustomResultId> Insert<T>(T model, bool isAuto = true) =>
            await ExecuteWriteAsync(connection => connection.Insert(model, SourceTableName, isAuto));

        /// <summary>
        /// 全量更新
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="model">动态实体类</param>
        /// <param name="whereExpression">判断条件</param>
        /// <returns></returns>
        public virtual async Task<CustomResult> UpdateAll<T>(T model,
            Expression<Func<T, bool>> whereExpression = null) => await ExecuteWriteAsync(connection =>
            connection.UpdateAll(model, whereExpression, SourceTableName));

        /// <summary>
        /// 部分字段的更新
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="model">动态实体类</param>
        /// <param name="updateExpression">更新字段new{m.Name,....} Or new{m.Name="",....}</param>
        /// <param name="whereExpression">判断条件，如果为空默认根据Id判断</param>
        /// <returns></returns>
        public virtual async Task<CustomResult> Update<T>(T model, Expression<Func<T, object>> updateExpression,
            Expression<Func<T, bool>> whereExpression = null) => await ExecuteWriteAsync(connection =>
            connection.UpdatePartail(model, updateExpression, SourceTableName, whereExpression));


        /// <summary>
        /// 软删除
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="model">动态实体</param>
        /// <param name="whereExpression">判断条件</param>
        /// <returns></returns>
        public virtual async Task<CustomResult> DeleteSoft<T>(T model = null,
            Expression<Func<T, bool>> whereExpression = null) where T : BaseModel
        {
            model.IsDeleted = true;
            return await ExecuteWriteAsync(connection =>
                connection.UpdatePartail(model, m => new { m.IsDeleted }, SourceTableName, whereExpression));
        }

        /// <summary>
        /// 获取单个实体对象
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="model">动态实体类</param>
        /// <param name="whereExpression">判断条件</param>
        /// <returns></returns>
        public virtual async Task<CustomResult<T>> Get<T>(T model = null, Expression<Func<T, bool>> whereExpression = null) where T : class => await ExecuteReadAsync(connection => connection.Get(SourceTableName, model, whereExpression));

        /// <summary>
        /// 列表查询
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="selectSql">查询语句，包含排序等</param>
        /// <param name="totalSql">查询数量语句，不需要排序</param>
        /// <param name="parameters">参数</param>
        /// <returns></returns>
        protected internal async Task<PageListResult<T>> GetList<T>(string selectSql, string totalSql,
            Dictionary<string, object> parameters) where T : class, new()
        {
            return await ExecuteReadResultAsync(async connection =>
            {
                var parameter = new DynamicParameters(parameters);
                var total = await connection.ExecuteScalarAsync<long>(totalSql, parameter);
                var list = await connection.QueryAsync<T>(selectSql, parameter);
                return new PageListResult<T>(total, list.ToList());
            });
        }

        #endregion
    }
}